package org.ihelpuoo.parser.segment;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.ihelpuoo.io.FileUtils;
import org.ihelpuoo.io.StreamUtils;
import org.ihelpuoo.lang.BooleanUtils;
import org.ihelpuoo.lang.LoaderException;
import org.ihelpuoo.lang.NutException;
import org.ihelpuoo.lang.ObjectUtils;
import org.ihelpuoo.lang.SegmentNotFoundException;
import org.ihelpuoo.lang.StringUtils;
import org.ihelpuoo.util.Properties;

/**
 * <b>SegmentFactory class:</b>
 * <hr color=grey size=1>
 *
 *
 *
 * @author zozoh  created @ 2006
 *
 */
public class CharSegmentFactory extends SegmentFactory
{

	private String						home;
	private String						extension;
	private boolean						isAutoReload;
	private Class						segmentClass;

	private Map<String, SegmentWrapper>	segments;

	@Override
	public void init(Properties config) throws NutException
	{
		super.init(config);
		home = (String)config.get("home");
		if (StringUtils.isBlank(home))
			throw new NutException("You must declare a valid home!");

		if (home.endsWith("/") || home.endsWith("\\"))
			home = home.substring(0,home.length() - 1);

		extension = (String)config.get("extension");
		if (StringUtils.isBlank(extension))
			throw new NutException("You must declare a valid file extension by property 'extension'!");

		String s = config.getString("reload");
		if (null != s)
			isAutoReload = BooleanUtils.parseBoolean(s);
		else
			isAutoReload = true;

		s = config.getString("type");
		if (null != s)
		{
			try
			{
				segmentClass = Class.forName(s);
			}
			catch (ClassNotFoundException e)
			{
				throw new NutException(e);
			}
			if (!ObjectUtils.isChildOf(segmentClass,Segment.class))
			{
				throw new NutException("!class \"" + s + "\" should implement interface \""
						+ Segment.class.getName() + "\"");
			}
		}
		else
			segmentClass = CharSegment.class;

		segments = new HashMap<String, SegmentWrapper>();
	}

	@Override
	public String getExtension()
	{
		return extension;
	}

	@Override
	public String getHome()
	{
		return home;
	}

	@Override
	public boolean isAutoReload()
	{
		return isAutoReload;
	}

	/**
	 * @param path
	 * @return
	 * @throws LoaderException 
	 */
	@Override
	public Segment create(String name) throws SegmentNotFoundException
	{
		return this.getTemplate(name).duplicate();
	}

	@Override
	public Segment getTemplate(String name) throws SegmentNotFoundException
	{
		SegmentWrapper sw = segments.get(name);
		if (sw == null)
		{
			synchronized (segments)
			{
				sw = segments.get(name);
				if (sw == null)
				{
					sw = new SegmentWrapper();
					File f = FileUtils.findFile(formatPath(name));
					if (null == f || !f.exists())
						throw new SegmentNotFoundException("Fail to find file " + name);
					reloadSegment(name,sw,f);
				}
			}
		}
		else if (isAutoReload)
		{
			File f = FileUtils.findFile(formatPath(name));
			if (null == f || !f.exists())
				throw new SegmentNotFoundException("Fail to find file " + name);

			if (f.lastModified() != sw.lastModified)
				synchronized (segments)
				{
					if (f.lastModified() != sw.lastModified)
						reloadSegment(name,sw,f);
				}
		}
		return sw.segment;
	}

	@Override
	public synchronized void setTemplate(String name, Segment seg)
			throws SegmentNotFoundException
	{
		this.getTemplate(name);
		SegmentWrapper sw = segments.get(name);
		sw.segment = seg;
	}

	@Override
	public boolean contains(String name)
	{
		if (!isAutoReload)
			return segments.containsKey(name);
		SegmentWrapper sw = segments.get(name);
		if (null == sw)
			return false;
		File f = FileUtils.findFile(formatPath(name));
		return f.lastModified() == sw.lastModified;
	}

	private void reloadSegment(String name, SegmentWrapper sw, File f)
			throws SegmentNotFoundException
	{
		InputStream ins = null;
		try
		{
			ins = new BufferedInputStream(new FileInputStream(f));
			sw.segment = parseSegment(ins);
			sw.lastModified = f.lastModified();
		}
		catch (Exception e)
		{
			StreamUtils.safeClose(ins);
			throw new SegmentNotFoundException(
					"Segemnt \"" + formatPath(name) + "\" fail to open!",e);
		}
		segments.put(name,sw);
	}

	protected Segment parseSegment(InputStream ins) throws Exception
	{
		Segment seg = (Segment)segmentClass.newInstance();
		seg.parse(ins);
		ins.close();
		ins = null;
		return seg;
	}

	/**
	 * @param name
	 * @return
	 * @throws FileNotFoundException 
	 */
	private String formatPath(String name)
	{
		// @ TODO ZZH: consider the env in fileType and home. 
		if (null == name)
			return null;
		if (null == home)
			return null;
		String path = name.replaceAll("[.]","/") + "." + extension;
		path = home + "/" + path;
		return path;
	}

	private class SegmentWrapper
	{
		long	lastModified;
		Segment	segment;
	}
}
